#include "ObjModel.h"


ObjModel::ObjModel(void)
{
  this->m_normals = 0;
  this->m_texCoords = 0;
  this->m_vertices = 0;
  this->m_totalVerts = 0;
}


ObjModel::~ObjModel(void)
{
  if(this->m_normals) delete[] this->m_normals;
  if(this->m_texCoords) delete[] this->m_texCoords;
  if(this->m_vertices) delete[] this->m_vertices;

  this->m_normals = 0;
  this->m_texCoords = 0;
  this->m_vertices = 0;
  this->m_totalVerts = 0;
}


bool ObjModel::LoadObj(char* fileName)
{
  std::ifstream fileStream;
  int fileSize = 0;

  fileStream.open(fileName, std::ifstream::in);
  if(fileStream.is_open() == false)
    return false;

  fileStream.seekg(0, std::ios::end);
  fileSize = fileStream.tellg();
  fileStream.seekg(0, std::ios::beg);

  if(fileSize <= 0)
    return false;

  char* buffer = new char[fileSize];

  if(buffer == 0)
    return false;

  memset(buffer, '\0', fileSize);

  TokenStream tokenStream, lineStream, faceStream;
  std::string tempLine, token;

  fileStream.read(buffer, fileSize);
  tokenStream.SetTokenStream(buffer);

  delete[] buffer;

  tokenStream.ResetStream();

  std::vector<float> verts, norms, texC;
  std::vector<int> faces;

  char lineDelimiters[2] = {'\n', ' '};
  int i = 0;
  while(tokenStream.MoveToNextLine(&tempLine))
  {
    ++i;
    lineStream.SetTokenStream((char*) tempLine.c_str());
    tokenStream.GetNextToken(0,0,0);

    if(!lineStream.GetNextToken(&token, lineDelimiters, 2))
      continue;

    if(strcmp(token.c_str(), "v") == 0)
    {
      lineStream.GetNextToken(&token, lineDelimiters, 2);
      verts.push_back((float) atof(token.c_str()));

      lineStream.GetNextToken(&token, lineDelimiters, 2);
      verts.push_back((float) atof(token.c_str()));

      lineStream.GetNextToken(&token, lineDelimiters, 2);
      verts.push_back((float) atof(token.c_str()));
    }
    else if(strcmp(token.c_str(), "vn") == 0)
    {
      lineStream.GetNextToken(&token, lineDelimiters, 2);
      norms.push_back((float) atof(token.c_str()));
      
      lineStream.GetNextToken(&token, lineDelimiters, 2);
      norms.push_back((float) atof(token.c_str()));
      
      lineStream.GetNextToken(&token, lineDelimiters, 2);
      norms.push_back((float) atof(token.c_str()));
    }
    else if(strcmp(token.c_str(), "vt") == 0)
    {
      lineStream.GetNextToken(&token, lineDelimiters, 2);
      texC.push_back((float) atof(token.c_str()));

      lineStream.GetNextToken(&token, lineDelimiters, 2);
      texC.push_back((float) atof(token.c_str()));
    }
    else if( strcmp( token.c_str( ), "f" ) == 0 )
    {
      char faceTokens[3] = { '\n', ' ', '/' };
      std::string faceIndex;
      faceStream.SetTokenStream( ( char* )tempLine.c_str( ) );
      faceStream.GetNextToken( 0, 0, 0 );
      for( int i = 0; i < 3; i++ )
      {
        faceStream.GetNextToken( &faceIndex, faceTokens, 3 );
        faces.push_back( ( int )atoi( faceIndex.c_str( ) ) );
        faceStream.GetNextToken( &faceIndex, faceTokens, 3 );
        faces.push_back( ( int )atoi( faceIndex.c_str( ) ) );
        faceStream.GetNextToken( &faceIndex, faceTokens, 3 );
        faces.push_back( ( int )atoi( faceIndex.c_str( ) ) );
      }
    }
    else if( strcmp( token.c_str( ), "#" ) == 0 )
    {
      int a = 0;
      int b = a;
    }
    token[0] = '\0';
  }

  int vIndex = 0, nIndex = 0, tIndex = 0;
  int numFaces = (int) faces.size() / 9;

  this->m_totalVerts = numFaces * 3;
  this->m_vertices = new float[this->m_totalVerts * 3];

  if((int) norms.size() != 0)
  {
    this->m_normals = new float[this->m_totalVerts * 3];
  }

  if((int) texC.size() != 0)
  {
    this->m_texCoords = new float[this->m_totalVerts * 2];
  }

  for(int f = 0; f < (int) faces.size(); f+=3)
  {
    this->m_vertices[vIndex + 0] = verts[( faces[f + 0] - 1 ) * 3 + 0];
    this->m_vertices[vIndex + 1] = verts[( faces[f + 0] - 1 ) * 3 + 1];
    this->m_vertices[vIndex + 2] = verts[( faces[f + 0] - 1 ) * 3 + 2];
    vIndex += 3;
    if(this->m_texCoords)
    {
      this->m_texCoords[tIndex + 0] = texC[( faces[f + 1] - 1 ) * 2 + 0];
      this->m_texCoords[tIndex + 1] = texC[( faces[f + 1] - 1 ) * 2 + 1];
      tIndex += 2;
    }
    if(this->m_normals)
    {
      this->m_normals[nIndex + 0] = norms[( faces[f + 2] - 1 ) * 3 + 0];
      this->m_normals[nIndex + 1] = norms[( faces[f + 2] - 1 ) * 3 + 1];
      this->m_normals[nIndex + 2] = norms[( faces[f + 2] - 1 ) * 3 + 2];
      nIndex += 3;
    }
  }

  verts.clear( );
  norms.clear( );
  texC.clear( );
  faces.clear( );
  return true;
}